﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Beacon.SPC
{
    /// <summary>
    /// 7 or more consecutive points without a change in direction.
    /// </summary>
    public class Trend :ISpcRule
    {
        #region Members
        private const int PointCount = 7;

        SpcControlLimit _limit = null;
        SpcControlSide _side = SpcControlSide.Both;
        SpcDataQueue _queue = new SpcDataQueue(PointCount);

        #endregion

        #region Constructor
        public Trend(SpcControlSide side, SpcControlLimit limit)
        {
            _side = side;
            _limit = limit;
        }
        #endregion

        #region ISpcRule Members

        /// <summary>
        /// return rule type
        /// </summary>
        /// <returns></returns>
        public SpcRules RuleType()
        {
            return SpcRules.Trend;
        }

        /// <summary>
        /// return rule point count
        /// </summary>
        /// <returns></returns>
        public int GetRulePointCount()
        {
            return PointCount;
        }

        /// <summary>
        /// add point
        /// </summary>
        /// <param name="data" type="double"></param>
        /// <returns> true if data is violated the rule, others false</returns>
        public bool AddPoint(double data)
        {
            _queue.Enqueue(data);
            bool rlt = violate();
            if (rlt)
                Reset();

            return rlt;
        }

        /// <summary>
        /// add point with sample size
        /// limits for data point will be calculated with sample size.
        /// </summary>
        /// <param name="data"></param>
        /// <param name="sampleSize"></param>
        /// <returns>true if data is violated the rule, others false</returns>
        public bool AddPoint(double data, double sampleSize)
        {
            _limit.Calculate((int)sampleSize);
            return AddPoint(data);
        }

        /// <summary>
        /// reset rule
        /// </summary>
        public void Reset()
        {
            _queue.Clear();
        }

        #endregion

        #region Private Functions

        /// <summary>
        /// travel through the queue to check.
        /// </summary>
        /// <returns></returns>
        private bool violate()
        {
            bool increase = true;
            bool decrease = true;

            double preData = double.NaN;

            if (_queue.Count < PointCount)
                return false;

            for (int i = 0; i < _queue.Count; i++)
            {
                if (i == 0)
                    preData = _queue.ElementAt(i);
                else
                {
                    double currData = _queue.ElementAt(i);

                    if (currData > preData)
                        decrease = false;
                    else if(currData < preData)
                        increase = false;

                    if (!decrease && !increase)
                        break;

                    preData = currData;
                }
            }

            switch (_side)
            {
                case SpcControlSide.Upper:
                    return increase;        
                case SpcControlSide.Lower:
                    return decrease;
                case SpcControlSide.Both:
                    return increase || decrease;
            }
            return false;
        }

        #endregion
    }
}
